package com.laxia.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.laxia.exception.BusinessException;
import com.laxia.exception.ExceptionDefinitions;
import com.laxia.filter.BodyReaderHttpServletRequestWrapper;
import com.laxia.model.Result;
import com.laxia.util.NumberUtil;
import com.laxia.util.SessionUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * controller 抛出的异常统一处理模块
 */
@Slf4j
@AllArgsConstructor
@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    private ExceptionDefinitions exceptionDefinitions;

    /**
     * 业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public Result handleCustomizedException(HttpServletRequest request, BusinessException ex) {
        log.warn("业务异常, url: {}, cookie:{}, 参数: {}, 异常信息: {}", getUrl(request), getCookie(request), getParams(request), ex.getMessage());
        return Result.error(ex.getCode(), ex.getMessage());
    }

    /**
     * 参数校验错误异常 400
     */
    @ExceptionHandler(BindException.class)
    public Result handleBindException(HttpServletRequest request, BindException ex) {
        log.error("参数校验异常, url: {}, 参数: {}", getUrl(request), getParams(request), ex);
        return generateErrorsResult(ex.getBindingResult().getAllErrors());
    }

    /**
     * 参数校验错误异常 400
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException ex) {
        log.error("参数校验异常, url: {}, 参数: {}", getUrl(request), getParams(request), ex);
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        String message = violations.size() > 0 ? violations.iterator().next().getMessage() : "";
        return exceptionDefinitions.errorResult(message);
    }

    /**
     * 请求方法不存在，返回404
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result handleException(HttpServletRequest request, NoHandlerFoundException ex) {
        log.error("请求方法不存在, url: {}, 参数: {}", getUrl(request), getParams(request), ex);
        return exceptionDefinitions.errorResult("请求方法不存在");
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException ex) {
        log.error("请求方式不支持, url: {}, 参数: {}, 错误信息: {}", getUrl(request), getParams(request), ex.getMessage());
        return exceptionDefinitions.errorResult("请求方式不支持");
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result handleHttpMediaTypeNotSupportedException(HttpServletRequest request, HttpMediaTypeNotSupportedException ex) {
        log.error("Content-Type不支持, url: {}, 参数: {}, 错误信息: {}", getUrl(request), getParams(request), ex.getMessage());
        return exceptionDefinitions.errorResult("content-type不支持");
    }


    /**
     * 参数校验错误异常 400
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException ex) {
        log.error("参数校验错误异常, url: {}, 参数: {}", getUrl(request), getParams(request), ex);
        return generateErrorsResult(ex.getBindingResult().getAllErrors());
    }

    /**
     * 参数校验错误异常 400
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handleMissingServletRequestParameterException(HttpServletRequest request, MissingServletRequestParameterException ex) {
        log.error("请求参数缺失, url:{}, 参数:{}, 缺失参数:{}", getUrl(request), getParams(request), ex.getParameterName());
        return exceptionDefinitions.errorResult("请求参数缺失");
    }

    /**
     * 数据不可读 400
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handleHttpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException ex) {
        log.error("数据不可读, url: {}, 参数: {}", getUrl(request), getParams(request), ex);
        return exceptionDefinitions.errorResult("数据不可读(非标准json结构)");
    }

    /**
     * 服务器不能处理的异常，返回500
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(HttpServletRequest request, Exception ex) {
        log.error("系统内部错误, url: {}, 参数: {}", getUrl(request), getParams(request), ex);
        return Result.error();
    }

    /**
     * 服务器不能处理的异常，返回500
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result handleException(HttpServletRequest request, IllegalArgumentException ex) {
        log.error("不合法的参数异常, url: {}, 参数: {}, 异常信息: {}", getUrl(request), getParams(request), ex.getMessage());
        return exceptionDefinitions.errorResult(ex.getMessage());
    }


    private Result generateErrorsResult(List<ObjectError> allErrors) {
        String message = allErrors.size() > 0 ? allErrors.get(0).getDefaultMessage() : "";
        return exceptionDefinitions.errorResult(message);
    }

    private String getUrl(HttpServletRequest request) {
        return request.getRequestURL().toString();
    }

    private String getCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        return JSON.toJSONString(cookies);

    }

    private String getParams(HttpServletRequest request) {

        Map<String, String[]> parameterMap = request.getParameterMap();
        JSONObject paramAll = new JSONObject();
        paramAll.put("requestParameter", parameterMap);
        try {
            Long currentUserId = SessionUtil.getCurrentUserId();
            if (NumberUtil.isPositive(currentUserId)) {
                paramAll.put("userId", currentUserId);
            }
            if (request instanceof BodyReaderHttpServletRequestWrapper) {
                paramAll.put("requestBody", ((BodyReaderHttpServletRequestWrapper) request).getRequestBody());
            }
        } catch (Exception e) {
            log.warn("requestBody解析失败: {}", e.getMessage());
        }
        return paramAll.toJSONString();
    }

}
